package com.bayss.core.service;


import com.bayss.core.entity.statistical.Statistical;
import com.bayss.core.entity.statistical.StatisticalExample;
import com.bayss.core.entity.statistical.mapper.StatisticalMapper;
import com.bayss.core.enums.RedisKey;
import com.bayss.core.enums.StatisticalType;
import com.bayss.core.model.PayCountModel;
import com.bayss.core.model.StatisticalModel;
import com.bayss.core.util.BigDecimalUtil;
import com.bayss.core.util.DokoDateUtil;
import com.bayss.core.util.RedisUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class StatisticalService {
    private static Logger logger = LogManager.getLogger();
    @Autowired
    private StatisticalMapper statisticalMapper;
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    @Autowired
    RedisUtil redisUtil;

    /**
     * 日环比
     */
    public Integer ratioDay() {
        try {
            StatisticalExample example = new StatisticalExample();
            //今日销售额
            example.createCriteria().andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey())
                    .andObgTimeEqualTo(dateFormat.format(new Date()));
            Statistical todaySales = statisticalMapper.getSum(example);

            //昨日销售额
            example = new StatisticalExample();
            example.createCriteria().andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey())
                    .andObgTimeEqualTo(dateFormat.format(DokoDateUtil.getYesterday()));
            Statistical yesterdaySales = statisticalMapper.getSum(example);
            if (yesterdaySales.getResult() == 0) {
                return null;
            }

            //日环比
            Double rateDay = BigDecimalUtil.div(BigDecimalUtil.sub(todaySales.getResult(), yesterdaySales.getResult()), yesterdaySales.getResult(), 2) * 100;
            return rateDay.intValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 周同比
     *
     * @return
     */
    public Integer ratioWeek() {
        try {
            StatisticalExample example = new StatisticalExample();
            //本周销售额
            Date start = DokoDateUtil.getFirstDayOfWeek(new Date());
            Date end = DokoDateUtil.getLastDayOfWeek(new Date());
            example.createCriteria().andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey())
                    .andObgTimeBetween(dateFormat.format(start), dateFormat.format(end));
            Statistical currWeekSales = statisticalMapper.getSum(example);

            //去年同周的销售额
            int weeks = DokoDateUtil.getWeekOfear(new Date());
            int lastYear = DokoDateUtil.getLastYear();
            start = DokoDateUtil.getFirstDayOfWeek(lastYear, weeks);
            end = DokoDateUtil.getLastDayOfWeek(lastYear, weeks);
            example = new StatisticalExample();
            example.createCriteria().andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey())
                    .andObgTimeBetween(dateFormat.format(start), dateFormat.format(end));
            Statistical lastWeekSales = statisticalMapper.getSum(example);
            if (lastWeekSales.getResult() == 0) {
                return null;
            }

            //周同比
            Double rateWeek = BigDecimalUtil.div(BigDecimalUtil.sub(currWeekSales.getResult(), lastWeekSales.getResult()), lastWeekSales.getResult(), 2) * 100;
            return rateWeek.intValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 趋势柱状图：销售额&下单量
     *
     * @param queryType 1-销售额；2-下单量
     * @param dateRange 1-本周；2-本月，3-本年
     * @return
     */
    public List<Statistical> barChar(int queryType, int dateRange) {
        StatisticalExample example = new StatisticalExample();
        StatisticalExample.Criteria criteria = example.createCriteria();
        this.setDateRage(criteria, dateRange);
        if (queryType == 1) {
            criteria.andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey());
        } else {
            criteria.andObgKeyEqualTo(StatisticalType.ORDER_COUNT.getKey());
        }
        List<Statistical> salesList = statisticalMapper.getSumGroupByMonth(example);
        return salesList;
    }

    //柱状图日期范围选项
    private void setDateRage(StatisticalExample.Criteria criteria, int dateRange) {
        if (dateRange == 1) {
            Date start = DokoDateUtil.getFirstDayOfWeek(new Date());
            Date end = DokoDateUtil.getLastDayOfWeek(new Date());
            criteria.andObgTimeBetween(dateFormat.format(start), dateFormat.format(end));
        } else if (dateRange == 2) {
            Date start = DokoDateUtil.getFirstDayOfMonth();
            Date end = DokoDateUtil.getLastDayOfMonth();
            criteria.andObgTimeBetween(dateFormat.format(start), dateFormat.format(end));
        } else if (dateRange == 3) {
            String start = Calendar.getInstance().get(Calendar.YEAR) + "-01-01";
            criteria.andObgTimeBetween(start, dateFormat.format(new Date()));
        }
    }

    /**
     * 总销售额 日均销售额 总支付笔数 日环比 周同比
     */
    public StatisticalModel statisticalInfo() {
        StatisticalModel model = new StatisticalModel();

        //总销售额
        StatisticalExample example = new StatisticalExample();
        example.createCriteria().andObgKeyEqualTo(StatisticalType.SALE_AMOUNT.getKey());
        Statistical totalSales = statisticalMapper.getSum(example);
        model.setTotalSales(totalSales.getResult().intValue());

        //日均销售额
        Statistical dayAgvSales = statisticalMapper.getAgvByDay(example);
        model.setAvgSalesOfDay(dayAgvSales.getResult().intValue());

        //总支付笔数
        example = new StatisticalExample();
        example.createCriteria().andObgKeyEqualTo(StatisticalType.PAYMENT_COUNT.getKey());
        Statistical totalPayCount = statisticalMapper.getSum(example);
        model.setPayCount(totalPayCount.getResult().intValue());

        //日环比
        model.setRatioDay(this.ratioDay());
        //周同比
        model.setRatioWeek(this.ratioWeek());
        return model;
    }

    /**
     * 支付笔数
     * @return
     */
    public PayCountModel getPayCountChart(){
        PayCountModel payCountModel = new PayCountModel();
        StatisticalExample example = new StatisticalExample();
        example.setOrderByClause("obg_time");
        StatisticalExample.Criteria criteria = example.createCriteria();
        // 7天内
        Date start = DokoDateUtil.getFirstDayOfWeek(new Date());
        Date end = DokoDateUtil.getLastDayOfWeek(new Date());
        criteria.andObgTimeBetween(dateFormat.format(start), dateFormat.format(end));
        criteria.andObgKeyEqualTo(StatisticalType.PAYMENT_COUNT.getKey());

        List<Statistical> list = statisticalMapper.selectByExample(example);
        List<Integer> valueList = list.stream().map(Statistical::getObgValue).collect(Collectors.toList());
        List<String> keysList = list.stream().map(Statistical::getObgTime).collect(Collectors.toList());
        int totalPayCount = list.stream().mapToInt(Statistical::getObgValue).sum();

        payCountModel.setPayCount(totalPayCount);
        payCountModel.setKeys(keysList);
        payCountModel.setValues(valueList);
        return payCountModel;
    }

    /**
     * 异步写入（Async连接池）
     *
     * @param statisticalType
     * @param count
     */
    @Async
    public void saveStatisticalInfo(StatisticalType statisticalType, Integer count) {
        String obgKey = statisticalType.getKey();
        String obgTime = DokoDateUtil.formatDate(LocalDate.now());
        String redisKey = RedisKey.STATISTICAL_FLAG + obgKey;

        Object val = redisUtil.get(redisKey);
        if (!String.valueOf(val).equals(obgTime)) {
            this.insertOrUpdate(obgKey, obgTime, count);
            redisUtil.set(redisKey, obgTime);
        } else {
            statisticalMapper.updateObgValue(obgKey, obgTime, count);
        }
    }

    private synchronized void insertOrUpdate(String key, String time, Integer count) {
        Statistical record = this.searchOne(key, time);
        if (record == null) {
            record = new Statistical();
            record.setCreateTime(new Date());
            record.setUpdateTime(new Date());
            record.setObgKey(key);
            record.setObgTime(time);
            record.setObgValue(count);
            statisticalMapper.insertSelective(record);
        } else {
            statisticalMapper.updateObgValue(key, time, count);
        }
    }

    public Statistical searchOne(String key, String time) {
        StatisticalExample example = new StatisticalExample();
        example.createCriteria().andObgKeyEqualTo(key).andObgTimeEqualTo(time);
        List<Statistical> list = statisticalMapper.selectByExample(example);
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.get(0);
    }
}
